/*
 * @Author: zhangshu
 * @email: shiliang.zhang@tuya.com
 * @LastEditors: Please set LastEditors
 * @file name: app_ias_zone.c
 * @Description: 
 * @Copyright: HANGZHOU TUYA INFORMATION TECHNOLOGY CO.,LTD
 * @Company: http://www.tuya.com
 * @Date: 2022-02-08
 * @LastEditTime: 2022-02-08
 */
#include "tal_memory.h"
#include "tal_attribute_rw.h"
#include "tal_network_mgr.h"
#include "app_common.h"

#include "app_ias_zone.h"
#include "app_ias_zone_attribute.h"

//enroll response code
typedef enum {
  IAS_ENROLL_RESPONSE_CODE_SUCCESS          = 0x00,
  IAS_ENROLL_RESPONSE_CODE_NOT_SUPPORTED    = 0x01,
  IAS_ENROLL_RESPONSE_CODE_NO_ENROLL_PERMIT = 0x02,
  IAS_ENROLL_RESPONSE_CODE_TOO_MANY_ZONES   = 0x03,
} IAS_ENROLL_RESPONSE_CODE_E;

//zone state, enroll or not enroll
typedef enum {
  IAS_ZONE_STATE_NOT_ENROLLED = 0x00,
  IAS_ZONE_STATE_ENROLLED     = 0x01,
} IAS_ZONE_STATE_E;

//invalid zone id 
#define IAS_ZONE_INVALID_ZONE_ID            0XFF

//current zone status
STATIC UINT16_T current_zone_status = 0;

//zone enroll response callback
STATIC VOID_T ias_zone_enroll_response_callback(UINT8_T ep, UINT8_T enroll_code, UINT8_T zone_id);
//send enroll request command from server to client
STATIC VOID_T ias_enroll_request_with_client(VOID_T);
//ias zone status change notification command fail callback
STATIC VOID_T ias_zone_send_result_fail(TAL_SEND_ST_E send_status, TAL_ZG_SEND_DATA_T *send_data);
//enroll success callback
STATIC VOID_T ias_zone_enroll_success_callback(VOID_T);
//enroll failure callback
STATIC VOID_T ias_zone_enroll_failure_callback(VOID_T);
//send zone status change notification
STATIC VOID_T ias_zone_status_change_notification(UINT8_T ep, UINT16_T delay_ms);
//write zone status and send zone status change notificaiton command after delay times
STATIC VOID_T write_zone_status_msg_with_notification(UINT16_T status, BOOL_T noti, UINT16_T delay_ms);

/**
 * @description: ias zone init after preheat time delay, gpio state may not stable during the preheat time
 * @param[in] none
 * @return: none
 */
VOID_T app_zone_control_preheat_init(VOID_T)
{
    write_ias_zone_type(IAS_ZONE_TYPE);
    write_ias_zone_status(0);
}

/**
 * @description: ias zone cluster command receive handler
 * @param[in] cmd: command id
 * @param[in] payload: command payload
 * @param[in] cmd: command payload length
 * @param[in] cmd_type: command type, single or group
 * @return: OPERATE_RET, success when OPRT_OK, or else
 */
OPERATE_RET ias_zone_cluster_server_command_handler(UCHAR_T cmd, UINT8_T *payload, UINT8_T payload_len, ZIGBEE_CMD_E cmd_type)
{
    switch (cmd)
    {
        case CMD_ZONE_ENROLL_RESPONSE_COMMAND_ID: {
            ias_zone_enroll_response_callback(IAS_ZONE_ENDPOINT,
                                              payload[0],
                                              payload[1]);  // get enroll reponse status
            break;
        }
        default:
            return OPRT_COM_ERROR;
    }
    return OPRT_OK;
}

/**
 * @description: ias zone cluster write cie attribute receive handler
 * @param[in] none
 * @return: none
 */
VOID_T ias_zone_cie_address_write_callback(VOID_T)
{
     ias_enroll_request_with_client();
     USER_PR_DEBUG("send enroll request\r\n");
}

/**
 * @description: ias zone enroll response callback func
 * @param [in] ep: endpoint of ias zone cluster
 * @param [in] enroll_code: enroll status response, IAS_ENROLL_RESPONSE_CODE_E
 * @param [in] zone_id: zone id CIE saved
 * @return: none
 */
STATIC VOID_T ias_zone_enroll_response_callback(UINT8_T ep, UINT8_T enroll_code, UINT8_T zone_id)
{
    if ( enroll_code == IAS_ENROLL_RESPONSE_CODE_SUCCESS ) {             //enroll success
        write_ias_zone_state(IAS_ZONE_STATE_ENROLLED);       //update ias zone state to enrolled
        write_ias_zone_id(zone_id);                                    //write valid zone id
        ias_zone_enroll_success_callback();
    } else {
        write_ias_zone_state(IAS_ZONE_STATE_NOT_ENROLLED);   //update ias zone state to not enrolled
        write_ias_zone_id(IAS_ZONE_INVALID_ZONE_ID);                   //write invalid zone id
        ias_zone_enroll_failure_callback();
    }
}

/**
 * @description: send enroll request func
 * @param[in] none
 * @return: none
 */
STATIC VOID_T ias_enroll_request_with_client(VOID_T)
{
    TAL_ZG_SEND_DATA_T send_data;
    UINT8_T i = 0;
    tal_system_memset(&send_data, 0, SIZEOF(TAL_ZG_SEND_DATA_T));

    send_data.zcl_id = ZCL_ID_ZONE_ENROLL_REQUEST;
    send_data.qos = QOS_VIP_0;
    send_data.addr.type.dev.cluster_id = CLUSTER_IAS_ZONE_CLUSTER_ID;
    send_data.direction = ZG_ZCL_DATA_SERVER_TO_CLIENT;
    send_data.frame_type = ZG_ZCL_FRAME_TYPE_SPEC_TO_CLUSTER;
    send_data.command_id = CMD_ZONE_ENROLL_REQUEST_COMMAND_ID;

    send_data.delay_time = 0;
    send_data.random_time = 50;

    send_data.addr.mode = SEND_MODE_DEV;
    send_data.addr.type.dev.dst_addr = TUYA_GATEWAY_ADDRESS;
    send_data.addr.type.dev.dst_ep = 0x01;
    send_data.addr.type.dev.src_ep = TUYA_PRIMARY_ENDPOINT;

    send_data.data.private.data[i++] = (UINT8_T)IAS_ZONE_TYPE;
    send_data.data.private.data[i++] = IAS_ZONE_TYPE >> 8;
    send_data.data.private.data[i++] = (UINT8_T)MANUFACTURER_CODE;
    send_data.data.private.data[i++] = MANUFACTURER_CODE >> 8;
    send_data.data.private.len = i;

    tal_zg_clear_send_data(ZG_CLEAR_ALL_ZCL_ID, &send_data.zcl_id);
    tal_zg_send_data(&send_data, NULL, 3000);
}

/**
 * @description: ias zone enroll request send fail callback
 * @param: {in} send_status: send status
 * @param: {in} send_data: send data point
 * @return: none
 */
STATIC VOID_T ias_zone_send_result_fail(TAL_SEND_ST_E send_status, TAL_ZG_SEND_DATA_T *send_data)
{
    USER_PR_DEBUG("zone status change notification send status %d\r\n", send_status);
}

/**
 * @description: enroll success, user callback
 * @param[in] none
 * @return: none
 */
STATIC VOID_T ias_zone_enroll_success_callback(VOID_T)
{
    USER_PR_DEBUG("zone enroll success!!!!!!!!!!!!!!!!!!!!!!!!11\r\n");
    ias_zone_status_change_notification(IAS_ZONE_ENDPOINT, 1000);                  // update zone status
}

/**
 * @description: enroll failure, user callback
 * @param[in] none
 * @return: none
 */
STATIC VOID_T ias_zone_enroll_failure_callback(VOID_T)
{
    USER_PR_DEBUG("zone enroll failure!!!!!!!!!!!!!!!!!!!!!!!!11\r\n");
}

/**
 * @description: send zone status change notification command
 * @param [in] ep: endpoint of ias zone cluster
 * @param [in] delay_ms delay time in ms
 * @return: none
 */
STATIC VOID_T ias_zone_status_change_notification(UINT8_T ep, UINT16_T delay_ms)
{
    TAL_ZG_SEND_DATA_T send_data;
    UINT8_T i = 0;

    tal_system_memset(&send_data, 0, SIZEOF(TAL_ZG_SEND_DATA_T));

    send_data.zcl_id = ZCL_ID_ZONE_STATUS_CHANGE_NOTIFICATION;
    send_data.qos = QOS_1;
    send_data.direction = ZG_ZCL_DATA_SERVER_TO_CLIENT;
    send_data.frame_type = ZG_ZCL_FRAME_TYPE_SPEC_TO_CLUSTER;
    send_data.addr.type.dev.cluster_id = CLUSTER_IAS_ZONE_CLUSTER_ID;
    send_data.command_id = CMD_ZONE_STATUS_CHANGE_NOTIFICATION_COMMAND_ID;

    send_data.delay_time = delay_ms;
    send_data.random_time = 50;

    send_data.addr.mode = SEND_MODE_DEV;
    send_data.addr.type.dev.dst_addr = TUYA_GATEWAY_ADDRESS;
    send_data.addr.type.dev.dst_ep = 0x01;
    send_data.addr.type.dev.src_ep = TUYA_PRIMARY_ENDPOINT;

    send_data.data.private.data[i++] = current_zone_status;
    send_data.data.private.data[i++] = current_zone_status >> 8;
    send_data.data.private.data[i++] = 0;
    send_data.data.private.data[i++] = read_ias_zone_id();
    send_data.data.private.data[i++] = 0;
    send_data.data.private.data[i++] = 0;
    send_data.data.private.len = i;

    tal_zg_clear_send_data(ZG_CLEAR_ALL_ZCL_ID, &send_data.zcl_id);
    tal_zg_send_data(&send_data, &ias_zone_send_result_fail, 3000);
}

/**
 * @description: write zone status and send zone status change notification in delay ms
 * @param [in] zone_status: zone status of device
 * @param [in] noti: send change notification command or not
 * @param [in] delay_ms: delay time in ms
 * @return: none
 */
STATIC VOID_T write_zone_status_msg_with_notification(UINT16_T zone_status, BOOL_T noti, UINT16_T delay_ms)
{
    write_ias_zone_status(zone_status);

    current_zone_status = zone_status;
    if (noti) {
        ias_zone_status_change_notification(IAS_ZONE_ENDPOINT, delay_ms);
    }
}

/**
 * @description: set ias zone status bitmaps
 * @param [in] bit: zone status bit, please refer to ZONE_STATUS_BIT_E
 * @param [in] report: TRUE when need report, FALSE when not
 * @param [in] delay_ms: delay time in ms
 * @return: none
 */
VOID_T ias_zone_status_bit_set_with_report(ZONE_STATUS_BIT_E bit, BOOL_T report, UINT16_T delay_ms)
{
    if ((current_zone_status&bit) == bit)return;
    current_zone_status|=bit;
    if (report) {
        write_zone_status_msg_with_notification(current_zone_status, 1, delay_ms);
        return;
    }
    write_zone_status_msg_with_notification(current_zone_status, 0, 0);
}

/**
 * @description: clear ias zone status bitmaps
 * @param [in] bit: zone status bit, please refer to ZONE_STATUS_BIT_E
 * @param [in] report: TRUE when need report, FALSE when not
 * @param [in] delay_ms: delay time in ms
 * @return: none
 */
VOID_T ias_zone_status_bit_clear_with_report(ZONE_STATUS_BIT_E bit, BOOL_T report, UINT16_T delay_ms)
{
    if (!(current_zone_status&bit))return;
    current_zone_status = current_zone_status&(~bit);
    if (report) {
        write_zone_status_msg_with_notification(current_zone_status, 1, delay_ms);
        return;
    }
    write_zone_status_msg_with_notification(current_zone_status, 0, 0);
}

/**
 * @description: obtain device enroll state, enroll or not
 * @param[in] none
 * @return: TRUE when device already enrolled, FALSE when device in unenroll state
 */
BOOL_T is_device_enrolled(VOID_T)
{
    IAS_ZONE_STATE_E zonestate = IAS_ZONE_STATE_NOT_ENROLLED;

    zonestate = (IAS_ZONE_STATE_E)read_ias_zone_state();
    if (IAS_ZONE_STATE_ENROLLED == zonestate) {
        return TRUE;
    }
    USER_PR_DEBUG("device not enrolled!\r\n");
    return FALSE;
}

/**
 * @description: device in alarm state or not
 * @param[in] none
 * @return: TRUE when device in alarm state, FALSE when device normal
 */
BOOL_T is_in_alarm_state(VOID_T)
{
    if ((current_zone_status&IAS_ZONE_STATUS_BIT_ALARM_1) || (current_zone_status&IAS_ZONE_STATUS_BIT_ALARM_2)) {
        return TRUE;
    }
    return FALSE;
}

/**
 * @description: unenroll to clear ias zone info
 * @param[in] none
 * @return: none
 */
VOID_T ias_zone_unenroll(VOID_T)
{
     UINT8_T ieee_address[] = { 0, 0, 0, 0, 0, 0, 0, 0 };

     write_ias_zone_cie_address(ieee_address);
     write_ias_zone_type(IAS_ZONE_TYPE);
     write_ias_zone_id(IAS_ZONE_INVALID_ZONE_ID);
     write_ias_zone_state(IAS_ZONE_STATE_NOT_ENROLLED);
}


